package com.springcloud.redis.controller;

import com.springcloud.redis.utils.RedisUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;

import java.sql.Time;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author liyanhao
 * @Date 2022-11-25 9:59
 */
@RestController
@Slf4j
public class GoodController {
    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String KEY = "liyanhao_redis";

    @Value("${server.port}")
    private String serverPort;

    @Autowired
    private Redisson redisson;

    private Lock lock = new ReentrantLock();

    //分布式事物
    @SneakyThrows
    @GetMapping("/but_goods")
    public String buy_Goods() {
        RLock redissonLock = redisson.getLock(KEY);
        redissonLock.lock();

        try {
            //todo 业务
        } finally {
            if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {
                redissonLock.unlock();
            }
        }


        String value = UUID.randomUUID().toString() + Thread.currentThread().getName();
        //加锁考虑超时自动解锁问题：并且合并一行保持原子性：超时时间设置多少可以
        //加了超时时间之后，可能会出现误删问题:删除别人的锁,只能自己删除自己的
        //怎么知道缓存过期时间一定大于业务执行时间
        //缓存续命问题续约问题 redis集群是AP 高可用 redis单机是CP
        //主节点没有来得及把数据复制到从节点 导致数据丢失锁丢失 AP高可用 CP一致性 ZK集群是CP、在选举leader期间不可接收clients新的请求
        //主要是redis的集群主从之间的消息同步是异步的，而Zk集群主从之间的消息同步是同步的，所有实例都接收到消息之后才会返回发送者成功。
        //redis 多机异步复制AP zookeeper是多机同步复制CP
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(KEY, value, 10, TimeUnit.SECONDS);
        if (!flag) {
            return "抢锁失败,请重试";
        }

        try {
            //waittime 抢锁的等待时间，正常情况下 等3秒
            // leaseTime 就是redis key的过期时间，正常情况下等五分钟300秒
            boolean isLockBoolean = redissonLock.tryLock(3, 300, TimeUnit.SECONDS);
            log.info("线程{},是否拿到锁:{}", Thread.currentThread().getName(), isLockBoolean);
            if (isLockBoolean) {
                System.out.println(Thread.currentThread().getName() + "\t" + "----come in biz");
                //业务逻辑 忙十分钟
                TimeUnit.MILLISECONDS.sleep(10);
            }
        } catch (Exception e) {
            log.error("redlock exception ", e);
        }finally {
            //无论如何 最后都要解锁
            redissonLock.unlock();
        }


        //处理业务
        try {
            String key = "goods:001";
            String result = redisTemplate.opsForValue().get(key);
            int goodsNumber = result == null ? 0 : Integer.valueOf(result);
            if (goodsNumber > 0) {
                int realNumber = goodsNumber - 1;
                redisTemplate.opsForValue().set(key, realNumber + "");
                System.out.println("你已经成功秒杀商品，此时还剩余：" + realNumber + "件 \t 服务器端口号：" + serverPort);
                return "你已经成功秒杀商品，此时还剩余：" + realNumber + "件" + "\t 服务器端口：" + serverPort;
            } else {
                System.out.println("商品已经售罄/活动结束/调用超时，欢迎下次光临" + "\t 服务器端口：" + serverPort);
            }
        } finally {
            //只能自己删除自己的
             /*if (redisTemplate.opsForValue().get(KEY).equalsIgnoreCase(value)) {
                redisTemplate.delete(KEY);//释放锁
             }*/
            //使用lua脚本保持删除的原子性
            Jedis jedis = RedisUtils.getJedis();
            String script = "if redis.call('get',KEYS[1]) == ARGV[1] " +
                    "then " +
                    "return redis.call('del',KEYS[1])" +
                    "else " +
                    " return 0" +
                    "end";
            try {
                Object redis = jedis.eval(script, Collections.singletonList(KEY), Collections.singletonList(value));
                if ("1".equals(redis.toString())) {
                    System.out.println("----del KEY SUCCESS·");
                } else {
                    System.out.println("----del KEY ERROR");
                }
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }


        }
        return "商品已经售罄/活动结束/调用超时，欢迎下次光临" + "\t 服务器端口：" + serverPort;

    }
    //单机锁
   /* @GetMapping("/but_goods")
    public String buy_Goods() {
        String key = "goods:001";
        String result = redisTemplate.opsForValue().get(key);
        int goodsNumber = result == null ? 0 : Integer.valueOf(result);
        if (goodsNumber > 0) {
            int realNumber = goodsNumber - 1;
            redisTemplate.opsForValue().set(key, realNumber + "");
            System.out.println("你已经成功秒杀商品，此时还剩余：" + realNumber + "件 \t 服务器端口号：" + serverPort);
            return "你已经成功秒杀商品，此时还剩余：" + realNumber + "件" + "\t 服务器端口：" + serverPort;
        }else {
            System.out.println("商品已经售罄/活动结束/调用超时，欢迎下次光临"+"\t 服务器端口："+serverPort);
        }
        return "商品已经售罄/活动结束/调用超时，欢迎下次光临"+"\t 服务器端口："+serverPort;
    }

    public void m1() {
        //设置等待时间 会等待一会锁 如果还是没有获取不到就走 else
        try {
            if (lock.tryLock(1000, TimeUnit.SECONDS)) {

            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //不设置等待时间的加锁,直接走else
        if (lock.tryLock()) {
            try {
                lock.lock();

            } finally {
                lock.unlock();
            }
        } else {
            //没有抢到锁
        }
    }*/

}
